// pack_proof_abi_standard.js - 专业级ABI编码工具 for FISCO BCOS + WeBASE
// 功能：将PLONK proof.json和public.json转换为WeBASE兼容的ABI编码格式
// 作者：NZK_Asses项目组
// 创建时间：2024

const fs = require('fs');

/**
 * 🔧 从PLONK proof.json格式提取24个uint256元素
 * PLONK证明结构：
 * - 9个G1群元素：A, B, C, Z, T1, T2, T3, Wxi, Wxiw (每个3坐标 x,y,z)
 * - 6个标量元素：eval_a, eval_b, eval_c, eval_s1, eval_s2, eval_zw
 * - 我们需要24个uint256：18个坐标(9*2) + 6个标量 = 24个
 */
function extractProofElements(proofData) {
    console.log('🔧 开始提取PLONK proof元素...');
    const elements = [];
    
    // PLONK proof的9个G1点，按标准顺序
    const g1Points = ['A', 'B', 'C', 'Z', 'T1', 'T2', 'T3', 'Wxi', 'Wxiw'];
    
    // 提取G1点的坐标（只取x,y，忽略z=1）
    for (const pointName of g1Points) {
        if (proofData[pointName] && Array.isArray(proofData[pointName])) {
            const point = proofData[pointName];
            if (point.length >= 2) {
                elements.push(point[0]); // x坐标
                elements.push(point[1]); // y坐标
                console.log(`   ✓ ${pointName}: [${point[0].slice(0,20)}..., ${point[1].slice(0,20)}...]`);
            } else {
                console.warn(`   ⚠️ 点 ${pointName} 坐标不足，跳过`);
            }
        } else {
            console.warn(`   ⚠️ 缺少点 ${pointName}`);
        }
    }
    
    // 添加标量字段（评估值）
    const scalarFields = ['eval_a', 'eval_b', 'eval_c', 'eval_s1', 'eval_s2', 'eval_zw'];
    for (const field of scalarFields) {
        if (proofData[field]) {
            elements.push(proofData[field]);
            console.log(`   ✓ ${field}: ${proofData[field].slice(0,20)}...`);
        } else {
            console.warn(`   ⚠️ 缺少标量 ${field}`);
        }
    }
    
    console.log(`📊 共提取了 ${elements.length} 个proof元素`);
    
    // 确保正好24个元素
    if (elements.length > 24) {
        console.log(`⚠️  截取前24个元素（原有${elements.length}个）`);
        return elements.slice(0, 24);
    } else if (elements.length < 24) {
        console.log(`⚠️  补零到24个元素（原有${elements.length}个）`);
        while (elements.length < 24) {
            elements.push('0');
        }
    }
    
    return elements;
}

/**
 * 🎯 手动实现标准ABI编码 - 完全兼容FISCO BCOS
 * 编码格式：abi.encode(uint256[24], uint256)
 * 
 * ABI编码规则：
 * 1. 固定长度数组uint256[24]：直接连续编码，每个uint256占32字节
 * 2. 单个uint256：32字节编码
 * 3. 总长度：24*32 + 32 = 800字节
 */
function standardABIEncode(proofArray, dataHash) {
    console.log('🔧 执行标准ABI编码...');
    
    let encoded = '';
    
    // 第一部分：编码uint256[24]数组（768字节）
    console.log('   📝 编码proof数组 (24个uint256)...');
    for (let i = 0; i < 24; i++) {
        const element = proofArray[i] || '0';
        try {
            // 转换为BigInt确保精度，然后编码为32字节十六进制
            const bigintValue = BigInt(element);
            const hex = bigintValue.toString(16).padStart(64, '0');
            encoded += hex;
            
            if (i < 3 || i >= 21) { // 显示前3个和后3个元素
                console.log(`     [${i}]: ${element.slice(0,20)}... -> ${hex.slice(0,16)}...`);
            } else if (i === 3) {
                console.log(`     ... (显示中间元素省略) ...`);
            }
        } catch (error) {
            console.error(`❌ 编码第${i}个元素失败: ${element}`);
            throw error;
        }
    }
    
    // 第二部分：编码dataHash (32字节)
    console.log('   📝 编码dataHash...');
    try {
        const dataHashBigInt = BigInt(dataHash);
        const dataHashHex = dataHashBigInt.toString(16).padStart(64, '0');
        encoded += dataHashHex;
        console.log(`     DataHash: ${dataHash.slice(0,20)}... -> ${dataHashHex.slice(0,16)}...`);
    } catch (error) {
        console.error(`❌ 编码dataHash失败: ${dataHash}`);
        throw error;
    }
    
    const result = '0x' + encoded;
    console.log(`✅ ABI编码完成！总长度: ${result.length} 字符 (${(result.length-2)/2} 字节)`);
    
    return result;
}

/**
 * 🚀 主要编码函数 - 完整的转换流程
 */
function encodeProofForWeBASE() {
    console.log('🚀 开始PLONK proof到WeBASE格式转换...\n');
    
    try {
        // 1. 读取输入文件
        console.log('📂 Step 1: 读取输入文件...');
        if (!fs.existsSync('proof.json')) {
            throw new Error('缺少文件: proof.json');
        }
        if (!fs.existsSync('public.json')) {
            throw new Error('缺少文件: public.json');
        }
        
        const proofData = JSON.parse(fs.readFileSync('proof.json', 'utf8'));
        const publicData = JSON.parse(fs.readFileSync('public.json', 'utf8'));
        
        console.log('   ✓ proof.json 读取成功');
        console.log('   ✓ public.json 读取成功');
        
        // 2. 数据提取和验证
        console.log('\n📊 Step 2: 数据提取和验证...');
        const proofElements = extractProofElements(proofData);
        
        if (!Array.isArray(publicData) || publicData.length === 0) {
            throw new Error('public.json格式错误：应为包含dataHash的数组');
        }
        
        const dataHash = publicData[0];
        console.log(`🔢 DataHash (public signal): ${dataHash}`);
        console.log(`📋 Proof元素数量: ${proofElements.length}`);
        console.log(`📋 Proof示例 [0-2]: ${proofElements.slice(0, 3).map(e => e.slice(0,15) + '...').join(', ')}`);
        
        // 3. 执行ABI编码
        console.log('\n🔧 Step 3: 执行标准ABI编码...');
        const packedProof = standardABIEncode(proofElements, dataHash);
        
        // 4. 生成完整结果数据
        console.log('\n📦 Step 4: 生成结果数据...');
        const timestamp = new Date().toISOString();
        const result = {
            // 元信息
            metadata: {
                tool: "pack_proof_abi_standard.js",
                version: "2.0",
                timestamp: timestamp,
                description: "PLONK proof ABI encoding for FISCO BCOS + WeBASE"
            },
            
            // ABI编码结果
            abi_encoding: {
                packedProof: packedProof,
                format: "abi.encode(uint256[24], uint256)",
                byteLength: (packedProof.length-2)/2,
                compatibility: "FISCO BCOS + WeBASE v3.1.1+"
            },
            
            // 源数据信息
            source_data: {
                proofElementsCount: proofElements.length,
                dataHash: dataHash,
                proofProtocol: proofData.protocol || "plonk",
                curve: proofData.curve || "bn128",
                // 前6个proof元素作为验证样本
                proofSample: proofElements.slice(0, 6).map(e => ({
                    value: e,
                    preview: e.slice(0, 20) + "..."
                }))
            },
            
            // WeBASE调用信息
            webase_integration: {
                contractMethod: "verifyAssetZKPacked",
                parameterType: "bytes",
                parameterValue: packedProof,
                expectedGas: "300000",
                description: "使用ABI编码的proof数据调用零知识证明验证"
            },
            
            // 验证信息（后续填充）
            verification: {
                status: "pending",
                message: "等待验证..."
            }
        };
        
        // 5. 保存ABI编码结果
        const outputFile = 'abi_encoded_proof.json';
        fs.writeFileSync(outputFile, JSON.stringify(result, null, 2));
        console.log(`   ✓ 已保存结果到: ${outputFile}`);
        
        // 6. 生成WeBASE调用脚本
        console.log('\n📝 Step 5: 生成WeBASE调用脚本...');
        generateWeBASECallScript(result);
        
        // 输出摘要
        console.log('\n🎉 转换完成！文件生成摘要：');
        console.log(`📄 ${outputFile} - 完整的ABI编码结果和元数据`);
        console.log(`📄 webase_call_abi.sh - 可执行的WeBASE调用脚本`);
        
        console.log('\n📋 技术摘要：');
        console.log(`   📊 Proof元素数量: ${proofElements.length}`);
        console.log(`   🔢 DataHash: ${dataHash.slice(0,30)}...`);
        console.log(`   📏 编码后大小: ${(packedProof.length-2)/2} 字节`);
        console.log(`   🔧 编码格式: abi.encode(uint256[24], uint256)`);
        console.log(`   ✅ 兼容性: FISCO BCOS + WeBASE v3.1.1+`);
        
        return result;
        
    } catch (error) {
        console.error(`❌ 转换失败: ${error.message}`);
        console.error('堆栈信息:', error.stack);
        process.exit(1);
    }
}

/**
 * 📝 生成WeBASE调用脚本
 */
function generateWeBASECallScript(result) {
    const { packedProof } = result.abi_encoding;
    const { dataHash } = result.source_data;
    const timestamp = result.metadata.timestamp;
    
    const scriptContent = `#!/bin/bash
# WeBASE调用脚本 - 零知识证明验证
# 生成时间: ${timestamp}
# 工具版本: pack_proof_abi_standard.js v2.0

echo "🚀 执行零知识证明验证 (DataAsset.verifyAssetZKPacked)"
echo "📊 DataHash: ${dataHash.slice(0, 50)}..."
echo "📏 Proof大小: ${(packedProof.length-2)/2} 字节"
echo ""

# WeBASE API调用
curl -X POST --data '{
    "abiInfo": [
        {
            "inputs": [
                {
                    "name": "packedProof", 
                    "type": "bytes"
                }
            ],
            "name": "verifyAssetZKPacked", 
            "outputs": [
                {
                    "name": "", 
                    "type": "bool"
                }
            ],
            "stateMutability": "nonpayable",
            "type": "function"
        }
    ],
    "contractAddress": "YOUR_DATAASSET_CONTRACT_ADDRESS",
    "funcName": "verifyAssetZKPacked",
    "funcParam": ["${packedProof}"],
    "groupId": 1,
    "signUserId": "YOUR_SIGN_USER_ID"
}' http://localhost:5002/WeBASE-Front/trans/handle | jq

echo ""
echo "📊 调用信息摘要:"
echo "   🎯 方法: verifyAssetZKPacked"  
echo "   📝 参数类型: bytes"
echo "   📏 数据长度: ${(packedProof.length-2)/2} 字节"
echo "   🔢 DataHash: ${dataHash}"
echo "   🔧 编码格式: abi.encode(uint256[24], uint256)"
echo ""
echo "⚠️  请确保已更新脚本中的CONTRACT_ADDRESS和SIGN_USER_ID"
echo "✅ 若返回status=0且output包含true，则验证成功！"
`;
    
    const scriptFile = 'webase_call_abi.sh';
    fs.writeFileSync(scriptFile, scriptContent);
    fs.chmodSync(scriptFile, '755'); // 添加执行权限
    console.log(`   ✓ 已生成调用脚本: ${scriptFile}`);
    
    return scriptFile;
}

/**
 * 🔍 验证ABI编码正确性 - 确保往返编码一致
 */
function verifyABIEncoding(packedProof, originalProofElements, originalDataHash) {
    console.log('\n🔍 Step 6: 验证ABI编码正确性...');
    
    try {
        // 移除0x前缀
        const hexData = packedProof.slice(2);
        
        if (hexData.length !== 1600) { // 800字节 = 1600个十六进制字符
            throw new Error(`编码长度错误: 期望1600字符，实际${hexData.length}字符`);
        }
        
        // 解码proof数组（前768字节 = 24*32字节）
        console.log('   📊 解码proof数组...');
        const decodedProofElements = [];
        for (let i = 0; i < 24; i++) {
            const start = i * 64; // 每个uint256占64个十六进制字符
            const end = start + 64;
            const hexChunk = hexData.slice(start, end);
            const decodedValue = BigInt('0x' + hexChunk).toString();
            decodedProofElements.push(decodedValue);
            
            // 显示前3个和后3个元素的验证
            if (i < 3 || i >= 21) {
                const matches = decodedValue === originalProofElements[i];
                console.log(`     [${i}]: ${matches ? '✅' : '❌'} ${decodedValue.slice(0,20)}...`);
            }
        }
        
        // 解码dataHash（后32字节）
        console.log('   🔢 解码dataHash...');
        const dataHashHex = hexData.slice(24 * 64, 25 * 64);
        const decodedDataHash = BigInt('0x' + dataHashHex).toString();
        const hashMatches = decodedDataHash === originalDataHash;
        
        console.log(`     ${hashMatches ? '✅' : '❌'} DataHash: ${decodedDataHash.slice(0,30)}...`);
        
        // 全面验证
        let allProofMatches = true;
        for (let i = 0; i < 24; i++) {
            if (decodedProofElements[i] !== originalProofElements[i]) {
                console.error(`❌ Proof元素[${i}]不匹配:`);
                console.error(`   原始: ${originalProofElements[i]}`);
                console.error(`   解码: ${decodedProofElements[i]}`);
                allProofMatches = false;
            }
        }
        
        const verificationResult = {
            proofArrayMatch: allProofMatches,
            dataHashMatch: hashMatches,
            overallSuccess: allProofMatches && hashMatches,
            decodedElementsCount: decodedProofElements.length,
            details: {
                originalDataHash: originalDataHash,
                decodedDataHash: decodedDataHash,
                proofSampleMatches: decodedProofElements.slice(0, 3).map((val, idx) => 
                    val === originalProofElements[idx]
                )
            }
        };
        
        if (verificationResult.overallSuccess) {
            console.log('   ✅ 验证成功！编码解码完全一致');
        } else {
            console.error('   ❌ 验证失败！存在数据不匹配');
        }
        
        console.log(`   📊 解码统计: proof数组${decodedProofElements.length}元素, dataHash匹配${hashMatches}`);
        
        return verificationResult;
        
    } catch (error) {
        console.error(`❌ 验证过程出错: ${error.message}`);
        return {
            proofArrayMatch: false,
            dataHashMatch: false,
            overallSuccess: false,
            error: error.message
        };
    }
}

/**
 * 🎯 主函数 - 完整的工作流程
 */
function main() {
    console.log('🎯 PLONK Proof ABI编码工具 v2.0');
    console.log('🎯 目标: 将零知识证明转换为WeBASE兼容格式');
    console.log('🎯 兼容: FISCO BCOS + WeBASE v3.1.1+');
    console.log('=' .repeat(60));
    
    // 执行主要编码流程
    const result = encodeProofForWeBASE();
    
    // 执行验证
    const proofData = JSON.parse(fs.readFileSync('proof.json', 'utf8'));
    const publicData = JSON.parse(fs.readFileSync('public.json', 'utf8'));
    const originalProofElements = extractProofElements(proofData);
    const originalDataHash = publicData[0];
    
    const verification = verifyABIEncoding(
        result.abi_encoding.packedProof, 
        originalProofElements, 
        originalDataHash
    );
    
    // 更新结果中的验证信息
    result.verification = verification;
    fs.writeFileSync('abi_encoded_proof.json', JSON.stringify(result, null, 2));
    
    // 最终输出
    console.log('\n' + '=' .repeat(60));
    if (verification.overallSuccess) {
        console.log('🎉 ABI编码工具执行成功！');
        console.log('\n🎯 下一步操作指南：');
        console.log('1. 📄 查看 abi_encoded_proof.json 了解详细结果');
        console.log('2. 🏗️  部署包含 verifyAssetZKPacked 方法的 DataAsset.sol');
        console.log('3. ✏️  编辑 webase_call_abi.sh，填入合约地址和signUserId');
        console.log('4. 🚀 执行: ./webase_call_abi.sh');
        console.log('5. 🔍 检查WeBASE返回的验证结果');
        console.log('\n✅ 编码数据已准备就绪，可用于WeBASE调用！');
    } else {
        console.error('❌ ABI编码验证失败，请检查输入数据');
        process.exit(1);
    }
}

// 导出函数供测试使用
module.exports = {
    extractProofElements,
    standardABIEncode,
    encodeProofForWeBASE,
    verifyABIEncoding,
    generateWeBASECallScript
};

// 当直接运行时执行主函数
if (require.main === module) {
    main();
}